package com.core.image.tcp;

import com.core.image.core.Connector;
import com.core.image.core.box.StringRecevicePacket;
import com.core.image.core.impl.IdleTimeoutScheduleJob;
import com.core.image.core.impl.ScheduleJob;
import com.core.image.handler.ConnectorCloseChain;
import com.core.image.handler.ConnectorHandlerChain;
import com.core.image.handler.ConnectorStringPacketChain;
import com.core.image.utils.AudioRoom;
import com.core.image.utils.CloseUtils;
import com.core.image.utils.Foo;

import java.io.File;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * describe:
 *
 * @author 库琦
 * @date 2020/01/21
 */
public class TCPServer implements ServerAcceptor.AcceptListener, Group.GroupMessageAdapter {
    private final int port;
    private final File cachePath;
    private final List<ConnectorHandler> connectorHandlerList = new ArrayList<>();
    private final Map<String,Group> groups = new HashMap<>();
    private ServerAcceptor acceptor;
    private ServerSocketChannel server;
    private final ServerStatistics statistics = new ServerStatistics();
    public TCPServer(int port,File cachePath){
        this.port = port;
        this.cachePath =cachePath;
        this.groups.put(Foo.DEFAULT_GROUP_NAME, new Group(Foo.DEFAULT_GROUP_NAME, this));
    }
    boolean start(){
        try {
            ServerAcceptor acceptor = new ServerAcceptor(this);
            ServerSocketChannel server = ServerSocketChannel.open();
            server.configureBlocking(false);
            server.socket().bind(new InetSocketAddress(port));
            //注册客户端
            server.register(acceptor.getSelector(), SelectionKey.OP_ACCEPT);
            this.server = server;
            this.acceptor = acceptor;
            acceptor.start();
            if (acceptor.awaitRunning()) {
                System.out.println("服务器准备就绪～");
                System.out.println("服务器信息：" + server.getLocalAddress().toString());
                return true;
            } else {
                System.out.println("启动异常！");
                return false;
            }

        }catch (Exception e){
            e.printStackTrace();
            return false;

        }
    }
    void  broadcast(String str){
     ConnectorHandler[] connectorHandlers;
     synchronized (connectorHandlerList) {
         connectorHandlers = connectorHandlerList.toArray(new ConnectorHandler[0]);
     }
     for (ConnectorHandler connectorHandler : connectorHandlers) {
         sendMessageToClient(connectorHandler, str);
     }
 }
    void stop(){
        if (acceptor != null){
            acceptor.exit();
        }
        ConnectorHandler[] connectorHandlers;
        synchronized (connectorHandlerList){
            connectorHandlers = connectorHandlerList.toArray(new ConnectorHandler[0]);

            connectorHandlerList.clear();
        }
    for (ConnectorHandler connectorHandler : connectorHandlers) {
        connectorHandler.exit();
    }

    CloseUtils.close(server);
}

    /**
     * 新客户端链接时回调
     * @param channel
     */
    @Override
    public void onNewSocketArrived(SocketChannel channel) {
        try {
            ConnectorHandler connectorHandler = new ConnectorHandler(channel, cachePath);
            connectorHandler.getStringPacketChain()
                    .appendLast(statistics.stringPacketChain())
                    .appendLast(new ParseCommandConnectorStringPacketChain())
                    .appendLast(new ParseAudioStreamCommandStringPacketChain());

            // 添加关闭链接时的责任链
            connectorHandler.getCloseChain()
                    .appendLast(new RemoveAudioQueueOnConnectorClosedChain())
                    .appendLast(new RemoveQueueOnConnectorClosedChain());
            // 空闲任务调度
            ScheduleJob scheduleJob = new IdleTimeoutScheduleJob(10, TimeUnit.SECONDS, connectorHandler);
            connectorHandler.schedule(scheduleJob);
            synchronized (connectorHandlerList) {
                connectorHandlerList.add(connectorHandler);
                System.out.println("当前客户端数量：" + connectorHandlerList.size());
            }
        }catch (Exception e){

        }


    }
    /**
     * 获取当前的状态信息
     */
    Object[] getStatusString() {
        return new String[]{
                "客户端数量：" + connectorHandlerList.size(),
                "发送数量：" + statistics.sendSize,
                "接收数量：" + statistics.receiveSize
        };
    }

    @Override
    public void sendMessageToClient(ConnectorHandler handler, String msg) {
         handler.send(msg);
        statistics.sendSize++;

    }
    private ConnectorHandler findConnectorFromKey(String key) {
        synchronized (connectorHandlerList){
            for (ConnectorHandler connectorHandler:connectorHandlerList){
                if (connectorHandler.getKey().toString().equals(key)){
                    return connectorHandler;
                }
            }
        }
            return null;
    }
    // 房间映射表, 房间号-房间的映射
    private final HashMap<String, AudioRoom> audioRoomMap = new HashMap<>(50);
    // 链接与房间的映射表，音频链接-房间的映射
    private final HashMap<ConnectorHandler, AudioRoom> audioStreamRoomMap = new HashMap<>(100);
    //音频命令控制与数据流传输链接映射表
    private final HashMap<ConnectorHandler,ConnectorHandler> audioCmdToStreamMap = new HashMap<>(100);
    private final HashMap<ConnectorHandler,ConnectorHandler>  audioStreamToCmdMap = new HashMap<>(100);
    private ConnectorHandler findAudioStreamConnector(ConnectorHandler handler){
        ConnectorHandler connectorHandler = audioCmdToStreamMap.get(handler);
        if (connectorHandler == null){
            sendMessageToClient(handler,Foo.COMMAND_INFO_AUDIO_ERROR);
            return null;
        }
        return connectorHandler;
    }
    private AudioRoom createNewRoom() {
        AudioRoom room;
        do {
            room = new AudioRoom();
        }while (audioRoomMap.containsKey(room.getRoomCode()));
        //添加到缓存列表
        audioRoomMap.put(room.getRoomCode(),room);
        return room;
    }
    private boolean joinRoom(AudioRoom room,ConnectorHandler streamConnector){
        if (room.enterRoom(streamConnector)){
            audioStreamRoomMap.put(streamConnector,room);
            return true;
        }
            return false;
    }
    private void dissolveRoom(ConnectorHandler audioStreamConnector) {
        AudioRoom room = audioStreamRoomMap.get(audioStreamConnector);
        if (room == null){
            return;
        }
        ConnectorHandler[] connectors = room.getConnectors();
        for (ConnectorHandler conector :connectors){
            conector.unBindToBridge();
            audioStreamRoomMap.remove(conector);
            if (conector != audioStreamConnector ){
                sendStreamConnectorMessage(audioStreamConnector, Foo.COMMAND_INFO_AUDIO_STOP);
            }
        }
        //解散房间
        audioRoomMap.remove(room.getRoomCode());
    }
    /**
     * 给链接流对应的命令控制链接发送信息
     */
    private void sendStreamConnectorMessage(ConnectorHandler streamConnector, String msg) {
        if (streamConnector != null) {
            ConnectorHandler audioCmdConnector = findAudioCmdConnector(streamConnector);
            sendMessageToClient(audioCmdConnector, msg);
        }
    }
    private ConnectorHandler findAudioCmdConnector(ConnectorHandler handler){
            return audioStreamToCmdMap.get(handler);
    }
    private class RemoveAudioQueueOnConnectorClosedChain extends ConnectorCloseChain{
        @Override
        public boolean consume(ConnectorHandler clientHandler, Connector connector) {
            if (audioCmdToStreamMap.containsKey(clientHandler)){
                audioCmdToStreamMap.remove(clientHandler);
            }else if (audioStreamToCmdMap.containsKey(clientHandler)){
                audioStreamToCmdMap.remove(clientHandler);
                dissolveRoom(clientHandler);
            }
            return false;
        }
    }
    private class ParseAudioStreamCommandStringPacketChain extends ConnectorStringPacketChain{

        @Override
        public boolean consume(ConnectorHandler clientHandler, StringRecevicePacket packet) {
            String str = packet.entity();
            if (str.startsWith(Foo.COMMAND_CONNECTOR_BIND)){
                String key = str.substring(Foo.COMMAND_CONNECTOR_BIND.length());
                ConnectorHandler audioStreamConnector  = findConnectorFromKey(key);
                if (audioStreamConnector != null){
                    //添加绑定关系
                    audioCmdToStreamMap.put(clientHandler,audioStreamConnector);
                    audioStreamToCmdMap.put(audioStreamConnector,clientHandler);
                    //转化为桥接模式
                    audioStreamConnector.changeToBridge();
                }
            }else if (str.startsWith(Foo.COMMAND_AUDIO_CREATE_ROOM)){
                ConnectorHandler audioStreamConnector = findAudioStreamConnector(clientHandler);
                if (audioStreamConnector != null){
                    AudioRoom room = createNewRoom();
                    //加入一个客户端
                    joinRoom(room,audioStreamConnector);
                    sendMessageToClient(clientHandler,Foo.COMMAND_INFO_AUDIO_ROOM +room.getRoomCode());

                }
            }else if (str.startsWith(Foo.COMMAND_AUDIO_LEAVE_ROOM)){
                // 离开房间命令
                ConnectorHandler audioStreamConnector = findAudioStreamConnector(clientHandler);
                if (audioStreamConnector != null) {
                    // 任意一人离开都销毁房间
                    dissolveRoom(audioStreamConnector);
                    // 发送离开消息
                    sendMessageToClient(clientHandler, Foo.COMMAND_INFO_AUDIO_STOP);
                }
            }else if (str.startsWith(Foo.COMMAND_AUDIO_JOIN_ROOM)){
                ConnectorHandler audioStreamConnector = findAudioStreamConnector(clientHandler);
                if (audioStreamConnector != null){
                    String roomCode = str.substring(Foo.COMMAND_AUDIO_JOIN_ROOM.length());
                    AudioRoom room = audioRoomMap.get(roomCode);
                    if (room != null && joinRoom(room,audioStreamConnector)){
                        ConnectorHandler theOtherHandler = room.getTheOtherHandler(clientHandler);
                        theOtherHandler.bindToBridge(audioStreamConnector.getSender());
                        audioStreamConnector.bindToBridge(theOtherHandler.getSender());
                        // 给对方发送可开始聊天的消息
                        sendStreamConnectorMessage(theOtherHandler, Foo.COMMAND_INFO_AUDIO_START);

                    }else {
                        // 房间没找到，房间人员已满
                        sendMessageToClient(clientHandler, Foo.COMMAND_INFO_AUDIO_ERROR);
                    }

                }
            }else {
                return false;
            }
            return true;
        }





    }
    /**
     * 解析收到的消息，当前节点主要做命令的解析，
     * 如果子节点也未进行数据消费，那么则进行二次消费，直接返回收到的数据
     */
    private class ParseCommandConnectorStringPacketChain  extends ConnectorStringPacketChain {
        @Override
        public boolean consumeAgain(ConnectorHandler handler, StringRecevicePacket stringReceivePacket) {
            // 捡漏的模式，当我们第一遍未消费，然后又没有加入到群，自然没有后续的节点消费
            // 此时我们进行二次消费，返回发送过来的消息
            //sendMessageToClient(handler, stringReceivePacket.entity());
            return true;
        }
        @Override
        public boolean consume(ConnectorHandler clientHandler, StringRecevicePacket packet) {
           String str = packet.entity();
           if (str.startsWith(Foo.COMMAND_GROUP_JOIN)){
               Group group =groups.get(Foo.DEFAULT_GROUP_NAME);
               if (group.addMember(clientHandler)){
               sendMessageToClient(clientHandler,"Join Group:" + group.getName());
               }
               return true;
               }else if (str.startsWith(Foo.COMMAND_GROUP_LEAVE)){
               Group group = groups.get(Foo.DEFAULT_GROUP_NAME);
               if (group.removeMember(clientHandler)){
                   sendMessageToClient(clientHandler, "Leave Group:" + group.getName());
               }
               return true;
               }
                return false;

        }

    }
    private class RemoveQueueOnConnectorClosedChain extends ConnectorCloseChain {
        @Override
        public boolean consume(ConnectorHandler clientHandler, Connector connector) {
            synchronized (connectorHandlerList) {
                connectorHandlerList.remove(clientHandler);
            }
            // 移除群聊的客户端
            Group group = groups.get(Foo.DEFAULT_GROUP_NAME);
            group.removeMember(clientHandler);
            return true;
        }
    }

}
